#include "SceneExport.h"
#include "Common.h"
#include "../Program.h"
#include "../entity/EntityLight.h"
#include "../entity/EntityCamera.h"
#include <QDir>

/**
 * Android 平台下插件地址
 */
#define ANDROID_PLUGIN_DIR "/data/user/0/com.luoyexunxue.avatar/app_plugins/"

/**
 * @brief 导出场景
 * @param file 文件夹或ZIP文件
 */
void SceneExport::Export(const QString &file)
{
    QFileInfo fileInfo(file);
    bool dirExport = fileInfo.isDir();
    if (dirExport) _targetDir = file;
    else _targetDir = fileInfo.absolutePath() + QDir::separator() + ".avatar_tmp";
    _workDir = QFileInfo(Program::WorkingSpace).absolutePath();
    _assetLUT.clear();
    Common::CopyDirectory(Program::MediaDirectory, _targetDir, "avatar.lua", true);
    Common::CopyDirectory(_workDir, _targetDir, ".avatarproj", false);
    exportScript();
    if (!dirExport)
    {
        QStringList fileList;
        Common::ListAllFile(_targetDir, "", fileList);
        AvatarLibrary::avatar_compress(_targetDir, fileList, file);
        QDir(_targetDir).removeRecursively();
    }
}

/**
 * @brief 获取排序后的场景列表，保证父节点始终在前面
 * @return
 */
QList<std::shared_ptr<EntitySceneNode>> SceneExport::SortedSceneNode()
{
    QList<std::shared_ptr<EntitySceneNode>> sortList;
    QMapIterator<QString, std::shared_ptr<EntitySceneNode>> iter(Program::SceneNodeList);
    while (iter.hasNext())
    {
        iter.next();
        sortList.append(iter.value());
    }
    for (qsizetype i = 0; i < sortList.count(); i++)
    {
        std::shared_ptr<EntitySceneNode> current = sortList[i];
        if (current->Parent != nullptr)
        {
            for (qsizetype j = i + 1; j < sortList.count(); j++)
            {
                if (current->Parent == sortList[j].get())
                {
                    sortList.removeAt(i);
                    sortList.insert(j, current);
                    i -= 1;
                    break;
                }
            }
        }
    }
    return sortList;
}

/**
 * @brief 导出脚本
 */
void SceneExport::exportScript()
{
    QStringList entryScript;
    entryScript.append("OnReady = function()");
    entryScript.append("\tpackage.path = package.path..';'..engine.directory()..'?.lua'");
    // 处理插件
    entryScript.append("\tif engine.info().platform == 'android' then");
    entryScript.append("\t\tgui.scale(2)");
    foreach (const QString& plugin, Program::Config.PluginUsed) entryScript.append(QString("\t\tengine.plugin('%1%2.so')").arg(ANDROID_PLUGIN_DIR, plugin));
    entryScript.append("\tend");
    // 设置项
    Program::CameraParam.Update();
    entryScript.append(QString("\tgraphics.background(%1,1.0)").arg(Program::Config.Background.ToString()));
    entryScript.append(QString("\tphysics.gravity(%1)").arg(Program::Config.Gravity.ToString()));
    entryScript.append(QString("\tengine.input('map',%1,%2,%3)").arg(Program::Config.InputMouseMap[0]).arg(Program::Config.InputMouseMap[1]).arg(Program::Config.InputMouseMap[2]));
    entryScript.append(QString("\tengine.input('speed',%1,%2)").arg(Program::Config.InputSpeed[0]).arg(Program::Config.InputSpeed[1]));
    entryScript.append(QString("\tengine.input('disable',%1,%2,%3)").arg(Program::Config.InputDisable[0]).arg(Program::Config.InputDisable[1]).arg(Program::Config.InputDisable[2]));
    entryScript.append(Program::CameraParam.Script("\t"));
    entryScript.append(Program::LightParam.Script("\t"));
    // 其他项
    Common::WriteAllText(_targetDir, "__load.lua", serializeScene());
    entryScript.append("\tengine.script('__load.lua')");
    for (qsizetype i = 0; i < Program::UserScript.size(); i++)
    {
        QString name = QString("__user_%1.lua").arg(i);
        QString title = Program::UserScript[i].first;
        QString content = Program::UserScript[i].second;
        Common::WriteAllText(_targetDir, name, "-- " + title + "\n" + content);
        entryScript.append("\tengine.script('" + name + "')");
    }
    entryScript.append("end");
    entryScript.append("OnUpdate = function(dt)");
    entryScript.append("\tif updateLight ~= nil then updateLight() end");
    entryScript.append("end");
    Common::WriteAllText(_targetDir, "avatar.lua", entryScript.join('\n'));
}

/**
 * @brief 序列化场景节点
 * @return
 */
QString SceneExport::serializeScene()
{
    QStringList str;
    QList<std::shared_ptr<EntitySceneNode>> orderList = SortedSceneNode();
    for (qsizetype i = 0; i < orderList.size(); i++)
    {
        std::shared_ptr<EntitySceneNode> sceneNode = orderList.at(i);
        QString scriptInsert = sceneNode->ScriptInsert();
        QString scriptTransform = sceneNode->ScriptTransform();
        QString scriptMaterial = sceneNode->ScriptMaterial();
        QString scriptRenderMode = sceneNode->ScriptRenderMode();
        extractAsset(scriptInsert);
        str.append(scriptInsert);
        str.append(scriptTransform);
        if (!scriptMaterial.isEmpty()) str.append(scriptMaterial);
        if (!sceneNode->Material.IsCullFace || sceneNode->Material.IsAddColor || !sceneNode->Material.IsUseDepth || sceneNode->Material.IsWireframe) str.append(scriptRenderMode);
        str.append("");
    }
    // 等待新增节点脚本执行完再执行附加脚本
    for (qsizetype i = 0; i < orderList.size(); i++)
    {
        std::shared_ptr<EntitySceneNode> sceneNode = orderList.at(i);
        QString scriptPhysical = sceneNode->ScriptPhysical();
        if (!scriptPhysical.isEmpty()) str.append(scriptPhysical);
        str.append(sceneNode->ExtraScript);
        str.append("");
    }
    return str.join('\n');
}

/**
 * @brief 提取脚本字符串的资源
 * @param script
 */
void SceneExport::extractAsset(QString &script)
{
    const static QStringList keywords = {
        "texture",
        "textureNight",
        "meshFile",
        "splatFile",
        "soundFile",
        "blendMask",
        "blendTexture",
        "trunkTexture",
        "leafTexture",
        "maskMap",
        "heightMap",
        "normalMap",
        "distortionMap",
        "distributionMap",
        "colorMap",
        "flowMap"
    };
    for (qsizetype i = 0; i < keywords.size(); i++)
    {
        int index = 0;
        while ((index = script.indexOf(keywords[i], index)) > 0)
        {
            index += keywords[i].length();
            QString sub = script.mid(index).trimmed();
            if (!sub.startsWith("=")) continue;
            int begIndex = sub.indexOf('\'') + 1;
            int endIndex = sub.indexOf('\'', begIndex);
            if (begIndex <= 0 || endIndex <= 0)
            {
                begIndex = sub.indexOf('"') + 1;
                endIndex = sub.indexOf('"', begIndex);
                if (begIndex <= 0 || endIndex <= 0) continue;
            }
            QStringList fileList = sub.mid(begIndex, endIndex - begIndex).split(',');
            foreach (QString item, fileList)
            {
                QString asset = item.trimmed();
                if (asset.isEmpty()) continue;
                if (!(asset.startsWith('/') || asset.contains(':'))) asset = QDir::cleanPath(_workDir + QDir::separator() + asset);
                if (!QFile::exists(asset)) continue;
                if (asset.indexOf(_workDir) == 0) asset = asset.mid(_workDir.length() + 1);
                else if (_assetLUT.contains(asset)) asset = _assetLUT[asset];
                else
                {
                    // 不在项目目录下面需要复制资源文件
                    QFileInfo fileInfo(asset);
                    QString fileName = fileInfo.fileName();
                    QString filePath = QDir::cleanPath(_targetDir + QDir::separator() + fileName);
                    while (QFile::exists(filePath))
                    {
                        fileName = "_" + fileName;
                        filePath = QDir::cleanPath(_targetDir + QDir::separator() + fileName);
                    }
                    QFile::copy(fileInfo.filePath(), filePath);
                    _assetLUT[asset] = fileName;
                    asset = fileName;
                }
                script = script.replace(item, asset);
            }
        }
    }
}
